﻿using habbo.Cryptography;
using habbo.Kernel.Commons;
using habbo.Kernel.Network;
using habbo.Kernel.Packets.com.sulake.habbo.communication.messages.incoming.handshake;
using habbo.Kernel.Packets.Interfaces;
using habbo.Kernel.Packets.Messages;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace habbo.Kernel.Packets
{
    class PacketManager
    {
        /// <summary>
        /// Storage of names from Info Events
        /// </summary>
        public Dictionary<string, int> InfoEvents;

        /// <summary>
        /// Storage of Invokers from Message Events
        /// </summary>
        public Dictionary<int, IMessageEvent> MessageEvents;

        public PacketManager()
        {
            InfoEvents = new Dictionary<string, int>();

            foreach (var Packet in typeof(Headers.MessageEventIds).GetFields())
            {
                var PacketId = (int)Packet.GetValue(0);
                var PacketName = Packet.Name;

                if (!InfoEvents.ContainsValue(PacketId))
                {
                    InfoEvents.Add(PacketName, PacketId);
                }
            }

            MessageEvents = new Dictionary<int, IMessageEvent>();

            foreach (Type Type in Assembly.GetCallingAssembly().GetTypes())
            {
                if (Type == null)
                {
                    continue;
                }

                if (Type.GetInterfaces().Contains(typeof(IMessageEvent)))
                {
                    var ConstructorInfo = Type.GetConstructor(new Type[] { });

                    if (ConstructorInfo != null)
                    {
                        var Constructed = ConstructorInfo.Invoke(new object[] { }) as IMessageEvent;

                        int Header = GetHeader(Constructed);

                        if (!MessageEvents.ContainsKey(Header) && Header > 0)
                        {
                            MessageEvents.Add(Header, Constructed);
                        }
                    }
                }
            }

            int ComposerIds = typeof(Headers.MessageComposerIds).GetFields().Count();

            SystemApp.ConsoleSystem.PrintLine("GLOBAL", "Loaded {0}/{1} MessageEvent(s), and {2} MessageComposer(s).", MessageEvents.Count, InfoEvents.Count, ComposerIds);
        }

        /// <summary>
        /// Returns an header from an InfoEvent.
        /// </summary>
        /// <param name="Event"></param>
        /// <returns></returns>
        public int GetHeader(IMessageEvent Event)
        {
            using (DictionaryAdapter<string, int> DA = new DictionaryAdapter<string, int>(InfoEvents))
            {
                return DA.TryPopValue(Event.GetType().Name);
            }
        }

        /// <summary>
        /// Returns an name of an InfoEvent.
        /// </summary>
        /// <param name="Header"></param>
        /// <returns></returns>
        public string GetName(int Header)
        {
            using (DictionaryAdapter<string, int> DA = new DictionaryAdapter<string, int>(InfoEvents))
            {
                return DA.TryPopKey(Header);
            }
        }

        public ClientPacket pak;

        /// <summary>
        /// Handles the bytes from an Session.
        /// </summary>
        /// <param name="Session"></param>
        /// <param name="Bytes"></param>
        public void ProcessBytes(Session Session, ref byte[] Bytes)
        {
            int pos = 0;

            while (pos < Bytes.Length)
            {
                try
                {

                    int MessageLength = HabboEncoding.DecodeInt32(new byte[] { Bytes[pos++], Bytes[pos++], Bytes[pos++], Bytes[pos++] });
                    if (MessageLength < 2 || MessageLength > 1024)
                    {
                        return;
                    }
                    int MessageId = HabboEncoding.DecodeInt16(new byte[] { Bytes[pos++], Bytes[pos++]});

                    byte[] Content = new byte[MessageLength - 2];

                    for (int i = 0; i < Content.Length && pos < Bytes.Length; i++)
                    {
                        Content[i] = Bytes[pos++];
                    }

                    if (MessageId == 542)
                    {
                        return;
                    }

                    pak = new ClientPacket(MessageId, Content);
                    IMessageEvent Event = MessageEvents[pak.Header];
                    if (MessageEvents.ContainsKey(pak.Header))
                    {
                        SystemApp.ConsoleSystem.PrintLine("[RCV][#{0}][" + GetName(pak.Header) + "] >> {1}", pak.Header, pak.ToString());
                        Event.Invoke(Session, pak);
                    }
                }
                catch (KeyNotFoundException)
                {
                    SystemApp.ConsoleSystem.PrintLine("[RCV][#{0}] >> Isn't in the keystore.", pak.Header);
                    return;
                }
                catch (IndexOutOfRangeException e1)
                {
                    SystemApp.ConsoleSystem.PrintLine("[RCV][IOOREX] >> " + e1.ToString());
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                    int MessageLength = HabboEncoding.DecodeInt32(new byte[] { Bytes[pos++], Bytes[pos++], Bytes[pos++], Bytes[pos++] });
                    int MessageId = HabboEncoding.DecodeInt16(new byte[] { Bytes[pos++], Bytes[pos++] });
                }
            }
        }
    }
}
